#include "lib_pbc.h"
#include <stdlib.h>
#include <string.h>

#define new_value(t)		do{t* ret=(t*)malloc(sizeof(t));memset(ret, 0,sizeof(t));return (void*)ret;}while(0)		

void* new_int32()		{new_value(int32_t);}

void free_value(void* p)	{free(p);}

void* new_int64()		{new_value(int64_t);}

void* new_float()			{new_value(float);}

void* new_bool()			{new_value(uint8_t);}

void* new_string()		{new_value(string_t);}


pbc_list newV_pbc_list(newM_t n_func, freeM_t f_func)
{
	pbc_list ret = {0};

	ret.new_func = n_func;
	ret.free_func = f_func;
	return ret;
}

void* add_pbc_list(pbc_list* pl)
{
	pbc_list_one* ret  =NULL;
	check_null(pl, NULL);

	if(pl->head==NULL)
	{
		ret = (pbc_list_one*)malloc(sizeof(pbc_list_one));
		pl->head = ret;
		memset(pl->head, 0, sizeof(pbc_list_one));
		pl->head->lo_data = pl->new_func();
		pl->end = pl->head;
		pl->read = pl->head;
	}
	else
	{
		ret = (pbc_list_one*)malloc(sizeof(pbc_list_one));
		pl->end->next = ret;
		memset(pl->end->next, 0, sizeof(pbc_list_one));
		pl->end->next->lo_data = pl->new_func();
		pl->end = pl->end->next;		
	}

	(pl->list_count)++;
	return ret->lo_data;
}

static void* next_pbc_list(pbc_list* pl)
{
	pbc_list_one* ret = NULL;
	check_null(pl, NULL);
	if(pl->read)
	{
		ret=pl->read;
		pl->read = pl->read->next;
		return ret->lo_data;
	}
	else
		return NULL;
}


void freeV_pbc_list(pbc_list* pl)
{
	if(pl==NULL)
		return;

	while(pl->head)
	{
		pbc_list_one* back = pl->head->next;
		pl->free_func( pl->head->lo_data );
		pl->head->lo_data=NULL;
		free(pl->head);
		pl->head = back;
	}

	memset(pl, 0, sizeof(pbc_list));
}

/* === pack() === */
/* Pack an unsigned 32-bit integer in base-128 encoding, and return the number of bytes needed:
   this will be 5 or less.  come from protobuf-c */
static size_t uint32_pack (uint32_t value, uint8_t *out)
{
	unsigned rv = 0;
	if (value >= 0x80)
    {
		out[rv++] = (uint8_t)(value | 0x80);
		value >>= 7;
		if (value >= 0x80)
        {
			out[rv++] = (uint8_t)(value | 0x80);
			value >>= 7;
			if (value >= 0x80)
            {
				out[rv++] = (uint8_t)(value | 0x80);
				value >>= 7;
				if (value >= 0x80)
                {
					out[rv++] = (uint8_t)(value | 0x80);
					value >>= 7;
                }
            }
        }
    }
	/* assert: value<128 */
	out[rv++] = (uint8_t)value;
	return rv;
}

/* Pack a 64-bit unsigned integer that fits in a 64-bit uint,
   using base-128 encoding.  come from protobuf-c*/
static size_t uint64_pack (uint64_t value, uint8_t *out)
{
  uint32_t hi = (uint32_t )(value>>32);
  uint32_t lo = (uint32_t )value;
  unsigned rv;
  if (hi == 0)
    return uint32_pack ((uint32_t)lo, out);
  out[0] = (uint8_t) ((lo) | 0x80);
  out[1] = (uint8_t) ((lo>>7) | 0x80);
  out[2] = (uint8_t) ((lo>>14) | 0x80);
  out[3] = (uint8_t) ((lo>>21) | 0x80);
  if (hi < 8)
    {
      out[4] = (uint8_t) ((hi<<4) | (lo>>28));
      return 5;
    }
  else
    {
      out[4] = (uint8_t) (((hi&7)<<4) | (lo>>28) | 0x80);
      hi >>= 3;
    }
  rv = 5;
  while (hi >= 128)
    {
      out[rv++] = (uint8_t) (hi | 0x80);
      hi >>= 7;
    }
  out[rv++] = (uint8_t)hi;
  return rv;
}

static size_t write_id(uint8_t *out, uint32_t id)
{
	if (id < (1<<(32-3)))
		return uint32_pack (id<<3, out);
	else
		return uint64_pack (((uint64_t)id) << 3, out);
}

/* return the zigzag-encoded 32-bit unsigned int from a 32-bit signed int . come frome protobuf-c*/
static  uint32_t zigzag32 (int32_t v)
{
	if (v < 0)
		return ((uint32_t)(-v)) * 2 - 1;
	else
		return v * 2;
}

/* Pack a 32-bit integer in zigwag encoding. come from protobuf-c*/
static size_t sint32_pack (int32_t value, uint8_t *out)
{
	return uint32_pack (zigzag32 (value), out);
}

/* Return the number of bytes required to store
   a variable-length unsigned integer that fits in 32-bit uint
   in base-128 encoding. come from protobuf-c*/
static  size_t uint32_size (uint32_t v)
{
  if (v < (1<<7))
    return 1;
  else if (v < (1<<14))
    return 2;
  else if (v < (1<<21))
    return 3;
  else if (v < (1<<28))
    return 4;
  else
    return 5;
}

/* return the zigzag-encoded 64-bit unsigned int from a 64-bit signed int. come from protobuf-c */
static  uint64_t zigzag64 (int64_t v)
{
	if (v < 0)
		return ((uint64_t)(-v)) * 2 - 1;
	else
		return v * 2;
}

/* Pack a 64-bit signed integer in zigzan encoding,
   return the size of the packed output.
   (Max returned value is 10). come from protobuf-c */
static  size_t sint64_pack (int64_t value, uint8_t *out)
{
  return uint64_pack (zigzag64 (value), out);
}


/* Pack a 32-bit value, little-endian.
   Used for fixed32, sfixed32, float). come from protobuf-c*/
static size_t fixed32_pack (uint32_t value, void *out)
{
#if IS_LITTLE_ENDIAN
  memcpy (out, &value, 4);
#else
  uint8_t *buf = out;
  buf[0] = (uint8_t) value;
  buf[1] = (uint8_t) (value>>8);
  buf[2] = (uint8_t) (value>>16);
  buf[3] = (uint8_t) (value>>24);
#endif
  return 4;
}

/*pack bool. come from protobuf-c*/
static  size_t boolean_pack (uint8_t value, uint8_t *out)
{
	*out = value ? 1 : 0;
	return 1;
}


/* Pack a length-prefixed string.
   The input string is NUL-terminated.

   The NULL pointer is treated as an empty string.
   This isn't really necessary, but it allows people
   to leave required strings blank.
   (See Issue 13 in the bug tracker for a 
   little more explanation). come from protobuf-c
 */
static  size_t string_pack (const char * str, uint8_t *out)
{
  if (str == NULL)
    {
      out[0] = 0;
      return 1;
    }
  else
    {
      size_t len = strlen (str)+1;	//string append \0
      size_t rv = uint32_pack (len, out);
      memcpy (out + rv, str, len);
      return rv + len;
    }
}

typedef size_t (*write_filed)(uint8_t* out, void* vp, uint32_t id);
static size_t write_repeated(uint8_t* out, pbc_list* vp, write_filed filed_func, uint32_t id)
{
	size_t rv=0;
	void* ptr=NULL;
	reset_pbc_list(vp);			// reset pbc list read ptr
	while(ptr=next_pbc_list((pbc_list*)vp))
		rv += filed_func(out+rv, ptr, id);
	reset_pbc_list(vp);
	return rv;
}

static size_t write_int32_filed(uint8_t* out, void* vp, uint32_t id)
{
	size_t rv = write_id(out, id);
	out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
	return rv + sint32_pack ( *((int32_t*)vp), out + rv);
}

/*PBC operator buffer */
size_t pbc_write_int32(uint8_t* out, e_pbc_label label, void* vp, uint32_t id)
{	
	int rv =0;
	switch(label)
	{
	case PROTOBUF_C_LABEL_REQUIRED:
	case PROTOBUF_C_LABEL_OPTIONAL:
		return write_int32_filed(out, vp, id);
	case PROTOBUF_C_LABEL_REPEATED:
		return write_repeated(out, vp, write_int32_filed, id);
	}

	return 0;
}

static size_t write_long_field(uint8_t* out, void* vp, uint32_t id)
{
	size_t rv = write_id(out, id);
	out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
	return rv + sint64_pack (*((int64_t*)vp), out + rv);
}

size_t pbc_write_long(uint8_t* out, e_pbc_label label, void* vp, uint32_t id)
{
	size_t rv=0;
	switch(label)
	{
	case PROTOBUF_C_LABEL_REQUIRED:
	case PROTOBUF_C_LABEL_OPTIONAL:
		return write_long_field(out, vp, id);
	case PROTOBUF_C_LABEL_REPEATED:
		return write_repeated(out, vp, write_long_field, id);
	}
	return 0;
}

static size_t write_float_field(uint8_t* out, void* vp, uint32_t id)
{
	size_t rv = write_id(out, id);
	out[0] |= PROTOBUF_C_WIRE_TYPE_32BIT;
	return	rv + fixed32_pack (*((const uint32_t *)(vp)), out + rv);
}

size_t pbc_write_float(uint8_t* out, e_pbc_label label, void* vp, uint32_t id)
{
	size_t rv =0;
	switch(label)
	{
	case PROTOBUF_C_LABEL_REQUIRED:
	case PROTOBUF_C_LABEL_OPTIONAL:
		return write_float_field(out, vp, id);
	case PROTOBUF_C_LABEL_REPEATED:
		return write_repeated(out, vp, write_float_field, id);
	}
	return 0;
}

static size_t write_bool_field(uint8_t* out, void* vp, uint32_t id)
{
	size_t rv = write_id(out, id);
	out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
	return rv + boolean_pack ( *((uint8_t*)vp), out + rv);
}

size_t pbc_write_bool(uint8_t* out, e_pbc_label label, void* vp, uint32_t id)
{
	switch(label)
	{
	case PROTOBUF_C_LABEL_REQUIRED:
	case PROTOBUF_C_LABEL_OPTIONAL:
		return write_bool_field(out, vp, id);
	case PROTOBUF_C_LABEL_REPEATED:
		return write_repeated(out, vp, write_bool_field, id);
	}
	return 0;
}

static size_t write_string_field(uint8_t* out, void* vp, uint32_t id)
{
	size_t rv = write_id(out, id);
	out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
	return rv + string_pack ( *((string_t*)vp), out + rv);
}

size_t pbc_write_string(uint8_t* out, e_pbc_label label, void* vp, uint32_t id)
{
	size_t rv = 0;

	if(label==PROTOBUF_C_LABEL_REQUIRED || (label==PROTOBUF_C_LABEL_OPTIONAL && *((string_t*)vp) ) )	
		return write_string_field(out, vp, id);
	else if(label==PROTOBUF_C_LABEL_REPEATED) 
		return write_repeated(out, vp, write_string_field, id);
	return 0;
}

static size_t write_message_field(uint8_t* out, void* vp, message_serial ms_func, uint32_t id)
{
	size_t rv = write_id(out, id);
	size_t p_rv = 0;
	uint32_t rv_packed_size = 0;
	out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
	
	p_rv = ms_func(vp, rv+out+1);
	rv_packed_size = uint32_size (p_rv);
	if(rv_packed_size > 1)
		memmove(out+rv_packed_size, out+1, p_rv);
	return uint32_pack(p_rv, out) + rv + p_rv;
}

size_t pbc_write_message(uint8_t* out, e_pbc_label label, void* vp, message_serial ms_func, uint32_t id)
{
	size_t rv=0;
	switch(label)
	{
	case PROTOBUF_C_LABEL_REQUIRED:
	case PROTOBUF_C_LABEL_OPTIONAL:
		return write_message_field(out, vp, ms_func, id);
	case PROTOBUF_C_LABEL_REPEATED:
		{
			void* ptr=NULL;
			reset_pbc_list((pbc_list*)vp);
			while(ptr=next_pbc_list((pbc_list*)vp))
				rv += write_message_field(out+rv, ptr, ms_func, id);
			reset_pbc_list((pbc_list*)vp);
		}
		return rv;
	}
	return 0;
}

size_t pbc_tag(uint8_t* in, size_t len, uint32_t* tag_out, ProtobufCWireType *wiretype_out)
{
	unsigned max_rv = len > 5 ? 5 : len;
	uint32_t tag = (in[0]&0x7f) >> 3;
	unsigned shift = 4;
	unsigned rv;
	*wiretype_out = in[0] & 7;
	
	if ((in[0] & 0x80) == 0)
    {
		*tag_out = tag;
		return 1;
    }
	for (rv = 1; rv < max_rv; rv++)
	{
		if (in[rv] & 0x80)
		{
			tag |= (in[rv] & 0x7f) << shift;
			shift += 7;
		}
		else
		{
			tag |= in[rv] << shift;
			*tag_out = tag;
			return rv + 1;
		}
	}
	return 0;                   /* error: bad header */
}

static  uint32_t parse_uint32 (size_t len, const uint8_t *data)
{
	unsigned rv = data[0] & 0x7f;
	if (len > 1)
    {
		rv |= ((data[1] & 0x7f) << 7);
		if (len > 2)
        {
			rv |= ((data[2] & 0x7f) << 14);
			if (len > 3)
            {
				rv |= ((data[3] & 0x7f) << 21);
				if (len > 4)
					rv |= (data[4] << 28);
            }
        }
    }
	return rv;
}

static  uint32_t parse_int32 (size_t len, const uint8_t *data)
{
	return parse_uint32 (len, data);
}

uint64_t parse_uint64 (unsigned len, const uint8_t *data)
{
	unsigned shift, i;
	uint64_t rv;
	if (len < 5)
		return parse_uint32 (len, data);
	rv = ((data[0] & 0x7f))
		| ((data[1] & 0x7f)<<7)
		| ((data[2] & 0x7f)<<14)
		| ((data[3] & 0x7f)<<21);
	shift = 28;
	for (i = 4; i < len; i++)
    {
		rv |= (((uint64_t)(data[i]&0x7f)) << shift);
		shift += 7;
    }
	return rv;
}

static  uint32_t parse_fixed_uint32 (const uint8_t *data)
{
#if IS_LITTLE_ENDIAN
	uint32_t t;
	memcpy (&t, data, 4);
	return t;
#else
	return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
#endif
}
static   uint64_t  parse_fixed_uint64 (const uint8_t *data)
{
#if IS_LITTLE_ENDIAN
	uint64_t t;
	memcpy (&t, data, 8);
	return t;
#else
	return (uint64_t)parse_fixed_uint32 (data)
		| (((uint64_t)parse_fixed_uint32(data+4)) << 32);
#endif
}

static uint8_t parse_boolean (unsigned len, const uint8_t *data)
{
	unsigned i;
	for (i = 0; i < len; i++)
		if (data[i] & 0x7f)
			return 1;
		return 0;
}


static size_t number_lens(uint8_t* in, size_t len)
{
	unsigned max_len = len < 10 ? len : 10;
	unsigned i;
	for (i = 0; i < max_len; i++)
	{
		if ((in[i] & 0x80) == 0)
			break;
	}
	if (i == max_len)
		return 0;
    return i + 1;		
}


size_t pbc_read_int32(uint8_t* in, size_t len, int32_t* des_p)
{	
	size_t lens = 0;

	check_fail(lens=number_lens(in, len), 0);
	*(uint32_t*)des_p = parse_int32(lens, in);
	return lens;
}

size_t pbc_read_uint32(uint8_t* in, size_t len, uint32_t* des_p)
{
	size_t lens = 0;
	
	check_fail(lens=number_lens(in, len), 0);
	*des_p = parse_uint32(lens, in);
	return lens;					
}

size_t pbc_read_long(uint8_t* in, size_t len, int64_t* des_p)
{
	size_t lens = 0;
	
	check_fail(lens=number_lens(in, len), 0);
	*(uint64_t*)des_p = parse_uint64(lens, in);
	return lens;
}

size_t pbc_read_float(uint8_t* in, size_t len, float* des_p)
{
	size_t lens = 0;
	
	check_fail(lens=number_lens(in, len), 0);
	*(uint32_t*)des_p = parse_fixed_uint32(in);
	return lens;
}

/*
size_t pbc_read_double(uint8_t* in, size_t len, double* des_p)
{
	size_t lens = 0;
	check_fail(lens=number_lens(in, len), 0);
	*(uint64_t*)des_p = parse_fixed_uint64(in);
	return lens;
}
*/

size_t pbc_read_bool(uint8_t* in, size_t len, uint8_t* des_p)
{
	size_t lens = 0;
	check_fail(lens=number_lens(in, len), 0);
	*des_p = parse_boolean(lens, in);
	return lens;
}

size_t pbc_read_string(uint8_t* in, size_t len, string_t* des_p)
{
	size_t rv =0;
	uint32_t lens = 0;
	check_fail(rv += pbc_read_uint32(in, len, &lens), 0);
	*des_p = (string_t)(in+rv);
	return rv+=lens;
}

size_t pbc_read_message(uint8_t* in, size_t len, void* des_p, message_parse parse_func)
{
	size_t rv =0;
	uint32_t lens = 0;
	
	check_fail(rv += pbc_read_uint32(in, len, &lens), 0);
	check_fail(rv += parse_func(des_p, in+rv, lens), 0);
	return rv;
}


